`timescale 1ns / 1ps

/**
 * @param a first 1-bit input
 * @param b second 1-bit input
 * @param g whether a and b generate a carry
 * @param p whether a and b would propagate an incoming carry
 */
module gp1(input wire a, b,
           output wire g, p);
   assign g = a & b;
   assign p = a | b;
endmodule

/**
 * Computes aggregate generate/propagate signals over a 4-bit window.
 * @param gin incoming generate signals
 * @param pin incoming propagate signals
 * @param cin the incoming carry
 * @param gout whether these 4 bits internally would generate a carry-out (independent of cin)
 * @param pout whether these 4 bits internally would propagate an incoming carry from cin 
 * @param cout the carry outs for the low-order 3 bits
 */
module gp4(input wire [3:0] gin, pin,
           input wire cin,
           output wire gout, pout,
           output wire [2:0] cout);

   // TODO: your code here

/* verilator lint_off UNOPTFLAT */
   wire [3:0] cout_temp;
   wire [3:0]gout_temp ;
/* verilator lint_off UNOPTFLAT */

   assign cout_temp[0] = gin[0] | (pin[0] & cin);

   assign cout_temp[1] = gin[1] | (pin[1] & gin[0]) | ((&pin[1:0]) & cin);

   assign cout_temp[2] = gin[2] | (pin[2] & gin[1]) | ((&pin[2:1]) & gin[0]) | ((&pin[2:0]) & cin); 

   assign cout_temp[3] = gin[3] | (pin[3] & gin[2]) | ((&pin[3:2]) & gin[1]) | ((&pin[3:1]) & gin[0]) | ((&pin[3:0]) & cin);

   assign cout = cout_temp[2:0];

   assign pout = &pin;

   genvar i;
   generate
      for(i=0;i<= 3;i=i+1) begin : cg_gen
         assign gout_temp[i] = (i==0) ? gin[0] : (gin[i] | (gout_temp[i-1] & pin[i]));
      end
   endgenerate

   assign gout = gout_temp[3];


endmodule

/** Same as gp4 but for an 8-bit window instead */
module gp8(input wire [7:0] gin, pin,
           input wire cin,
           output wire gout, pout,
           output wire [6:0] cout);

   // TODO: your code here


/* verilator lint_off UNOPTFLAT */
   wire [7:0] cout_temp;
   wire [7:0] gout_temp ;
/* verilator lint_off UNOPTFLAT */
   
   genvar i,k;
   generate
   for(i=0;i<=7;i=i+1) begin : cout_temp_gen
      assign cout_temp[i] = (i==0) ? cin    : (cout_temp[i-1] & pin[i-1]) | gin[i-1];
   end
   endgenerate

   generate
   for(k=0;k<=7;k=k+1) begin : cout_temp_gen
      assign gout_temp[k] = (k==0) ? gin[0] : (gin[k] | (gout_temp[k-1] & pin[k]));
   end
   endgenerate

   assign gout = gout_temp[7];

   assign cout = cout_temp[7:1];

   assign pout = &pin;



endmodule


module gpn #(
   parameter N = 4
)(
   input wire [N-1:0] gin, pin,
   input wire cin,
   output wire gout, pout,
   output wire [N-2:0] cout
);
/* verilator lint_off UNOPTFLAT */
   wire c [N-1:0];
   wire g [N-1:0];
/* verilator lint_on UNOPTFLAT */

   genvar i;
   generate
      for (i = 0; i <= N-1; i = i+1) begin : cg_gen
         assign c[i] = (i==0) ? cin    : gin[i-1] | (pin[i-1] & c[i-1]);
         assign g[i] = (i==0) ? gin[0] : gin[i]   | (pin[i]   & g[i-1]);
      end
   endgenerate
   
   generate
      for (i = 0; i <= N-2; i = i+1) begin : cout_gen
         assign cout[i] = c[i+1];
      end
   endgenerate

   assign gout = g[N-1];
   assign pout = &pin;

endmodule







module cla
  (input wire [31:0]  a, b,
   input wire         cin,
   output wire [31:0] sum);

   // TODO: your code here

   wire [31:0] c;
   wire [31:0]pin_1,gin_1;
   wire [7:0] cout;

   wire [7:0] pin_2,gin_2;

   wire pin_3,gin_3;



   // layer 1:for gp1
   genvar i;
   generate 
      for(i=0;i<32;i=i+1) begin : gp1_gen
         gp1 u_gp1( 
              .a(a[i]), 
              .b(b[i]),
              .g(gin_1[i]), 
              .p(pin_1[i])
         );
      end
 
   endgenerate

   assign c[0] = cin;

   //layer 2 :for pg4
   genvar k;
   generate
   for(k=0;k<8;k=k+1) begin:gp4_gen
      gpn #(
         .N(4)) 
      gp4_inst(
         .gin (gin_1[k*4 +: 4]),
         .pin (pin_1[k*4 +: 4]),
         .cin (c[4*k]),
         .gout(gin_2[k]),
         .pout(pin_2[k]),
         .cout(c[k*4+1 +: 3])
      );
   end
   endgenerate
 
   gpn #(
      .N(8)
   )
   gp8_inst(
      .gin (gin_2),
      .pin (pin_2),
      .cin (c[0]),
      .gout(gin_3),
      .pout(pin_3),
      .cout({c[28],c[24],c[20],c[16],c[12],c[8],c[4]})
   );


   // finally sum
   genvar j;
   generate
      for(j=0;j<32;j=j+1) begin : sum_gen
         assign sum[j] = a[j] ^ b[j] ^ c[j];
      end
   endgenerate


endmodule
